home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.2 Applications 1996 May / SGI IRIX 6.2 Applications 1996 May.iso / dist / impr_dev.idb / usr / impressario / src / drivers / laserjetPJL / laserjetPJL.c.z / laserjetPJL.c
C/C++ Source or Header  |  1996-05-06  |  40KB  |  1,192 lines

  1. /**************************************************************************
  2.  *
  3.  *                Copyright (c) 1992 Silicon Graphics, Inc.
  4.  *                      All Rights Reserved
  5.  *
  6.  *         THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI
  7.  *
  8.  * The copyright notice above does not evidence any actual of intended
  9.  * publication of such source code, and is an unpublished work by Silicon
  10.  * Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is
  11.  * the property of Silicon Graphics, Inc. Any use, duplication or
  12.  * disclosure not specifically authorized by Silicon Graphics is strictly
  13.  * prohibited.
  14.  *
  15.  * RESTRICTED RIGHTS LEGEND:
  16.  *
  17.  * Use, duplication or disclosure by the Government is subject to
  18.  * restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
  19.  * Technical Data and Computer Software clause at DFARS 52.227-7013,
  20.  * and/or in similar or successor clauses in the FAR, DOD or NASA FAR
  21.  * Supplement. Unpublished - rights reserved under the Copyright Laws of
  22.  * the United States. Contractor is SILICON GRAPHICS, INC., 2011 N.
  23.  * Shoreline Blvd., Mountain View, CA 94039-7311
  24.  **************************************************************************
  25.  *
  26.  * File: laserjetPJL.c
  27.  *
  28.  * Summary:
  29.  *    Print filter for HP LaserJet printers that support PJL.
  30.  *
  31.  * Description:
  32.  *    Build PJL based on command line options and pre-pends that to
  33.  *    data sent to printer.  Reads stdin and expects a stiff file that
  34.  *    is the image to print (can also read a PCL file as input).  stiff 
  35.  *    data converted to PCL data.
  36.  *
  37.  *    All output is sent to standard out.
  38.  *
  39.  * See Also:
  40.  *    Developers will want to be familiar with the SGI filter/driver spec
  41.  *    to understand exactly which options their drivers MUST implement,
  42.  *    how options are to be parsed, and which option letters are reserved.
  43.  *
  44.  *    Developers may be well advised to read the following man pages
  45.  *    for more information on the libraries and functions used in
  46.  *    this program:
  47.  *
  48.  *    libpod -- general information on POD database library
  49.  *    plp    -- information on the builtin parallel port interface
  50.  *
  51.  *    Specific libpod function calls are also documented, see their man pages.
  52.  *
  53.  **************************************************************************/
  54.  
  55. #ident "$Revision: 1.5 $"
  56.  
  57. #include "laserjetPJL.h"
  58. #include "pclpjl.h"
  59. #include "spool.h"
  60. #include "impr_versions.h"
  61.  
  62. /* Some global variables, mostly set by command line options */
  63.  
  64. /* This is size of buffer read and processed (rather then read in entire
  65.  * image it is read in BUFDEFSIZE chunks).
  66.  */
  67. #define BUFDEFSIZE      (1024*1024)
  68. static  int  InBufSize = BUFDEFSIZE;  /* size of buffer to allocate */
  69. static  char *InBuf = NULL;           /* start of input data buffer */
  70.  
  71. static int PrinterModel;              /* Printer model (4Mv, 5Si, etc.) */
  72.  
  73. static boolean StatusOnly = FALSE;   /* Exit after reporting status? */
  74. static boolean PrintWarnings = TRUE; /* Report warnings in status? */
  75. static boolean SendPJL = TRUE;       /* Send PJL commands ? */
  76.  
  77. static char    DEBUG;                /* What level debuging is turned on: */
  78. /* 0...3==none, COARSE-, MEDIUM-, FINEDETAIL */
  79.  
  80. static int NumCopies = 1;            /* Total total number of copies */
  81. static int HorizRes;                 /* Printer horizontal resolution. */
  82. static int VertRes;                  /* Printer vertical resolution. */
  83.  
  84. static int UserPageSize = LJ_DEFAULT_PAGE_SIZE;
  85.  
  86. static int Duplex = LJ_DEFAULT_DUPLEX;      /* Duplex option */
  87. static int Tray = LJ_DEFAULT_TRAY;          /* Tray to use */
  88. static int OutBin = LJ_DEFAULT_OUTBIN;      /* Output tray to use */
  89.  
  90. static int Darkness = LJ_DEFAULT_DARKNESS;  /* Darkness settings */
  91. static boolean FullBleed = FALSE;           /* Doing full-bleed B size */ 
  92.  
  93. static int pclFile = FALSE;                 /* True if input is already pcl */
  94.  
  95. static char *PrinterName = NULL;            /* Printer name is required */
  96.  
  97. static  FILE      *infile = stdin;          /* input from stdin or file */
  98.  
  99. /* Image file header information */
  100.  
  101. static PSTImageHeader ImageHeader;
  102. static STStream       *ImageStream = NULL;
  103.  
  104. /* The program name and version, for debug messages */
  105.  
  106. static char *ProgramName = NULL;
  107.  
  108. /* Printer POD information database structures */
  109.  
  110. static  PDInfoStruct    *Pod;           /* printer object database struct*/
  111. static  time_t           PodModTime;    /* time of last POD modification */
  112.  
  113. /* An array of error messages, set later */
  114.  
  115. static  PDMessageStruct  ErrMsgs[PD_MESSAGE_MAX];
  116.  
  117. /***********************************************************************/
  118.  
  119. void set_program_name(const char* name) {
  120.  
  121.     /* ASSUMES: nothing */
  122.     /* MODIFIES: global var ProgramName */
  123.     /* EFFECTS:  Sets global name of program (ProgramName). */
  124.     /*           Strips off any included path and leaves real name. */
  125.  
  126.     if ((ProgramName = strrchr(name,'/')) != NULL) ProgramName++;
  127.     else ProgramName = (char*)name;
  128. }
  129.  
  130. /***********************************************************************/
  131.  
  132. void  hold_signals(void) {
  133.  
  134.     /* REQUIRES: nothing
  135.      * MODIFIES: signal handling environment
  136.      * EFFECTS:  puts key signals on hold until release_signals is called.
  137.      *
  138.      * This function HOLDS the most common catchable interrupts.
  139.      * This is important in the UNIX environment because many
  140.      * software interrupts (signals) are possible.
  141.      */
  142.     (void)sighold(SIGINT);
  143.     (void)sighold(SIGTERM);
  144.     (void)sighold(SIGHUP);
  145.     (void)sighold(SIGQUIT);
  146. }
  147.  
  148. /***********************************************************************/
  149.  
  150. void  release_signals(void) {
  151.  
  152.     /* REQUIRES: nothing
  153.      * MODIFIES: signal handling environment
  154.      * EFFECTS:  releases key signals from any hold condition
  155.      *
  156.      * This function RELEASES the most common catchable interrupts.
  157.      * This is important in the UNIX environment because many
  158.      * software interrupts (signals) are possible.
  159.      */
  160.     (void)sigrelse(SIGINT);
  161.     (void)sigrelse(SIGTERM);
  162.     (void)sigrelse(SIGHUP);
  163.     (void)sigrelse(SIGQUIT);
  164. }
  165.  
  166. /***********************************************************************/
  167.  
  168. void print_usage_message(void)
  169. {
  170.     /* ASSUMES: nothing */
  171.     /* MODIFIES: stderr */
  172.     /* EFFECTS:  prints usage message */
  173.  
  174.     /* "/usr/lib/print/laserjetPJL- h" will print usage */
  175.   
  176.     fprintf(stderr, "\nUSAGE:\n%s -P PrinterName [options] [filename]\n\n",
  177.             ProgramName);
  178.  
  179.     fprintf(stderr,
  180.     "\tWhere [options] is one or more of:\n\n"
  181.     "\t[-s]        Update POD status files only, don't print.\n"
  182.     "\t[-X int]    Horizontal Resolution, in dpi (300 or 600).\n"
  183.     "\t[-Y int]    Vertical Resolution, in dpi (300 or 600).\n"
  184.     "\t[-S string] Paper Size (e.g., A, A4, Legal).\n"
  185.     "\t[-n int]    Number of copies.\n"
  186.     "\t[-d string] Set Duplex option.\n"
  187.     "\t             (string= \"off\",\"bindShortSide\",\"bindLongSide\").\n"
  188.     "\t[-j string] Set print darkness.\n"
  189.     "\t             (string= \"Normal\",\"Light\",\"Dark\",\"Economode\").\n"
  190.     "\t[-T string] Set media tray (string= \"upper\",\"lower\",\"either\",\"manual\").\n"
  191.     "\t[-R string] Ouput tray (string= \"top\",\"rear\").\n"
  192.     "\t[-D]        Debug mode, appends to logfile.\n"
  193.     "\t[-B int]    Internal buffer size in bytes.\n"
  194.     "\t[-h]        Print usage message.\n"
  195.     "\t[-J]        Do not send PJL commands. Use for printers that \n"
  196.     "\t            do not support PJL.  Some options may not work if\n"
  197.     "\t            this is used. (PJL used to set resolution, paper size, \n"
  198.     "\t            number of copies, duplex option, and paper tray to use.)\n"
  199.     "\t[-F string] Format of file to print. String can be 'pcl' or 'stiff'\n"
  200.     "\t            If 'pcl' driver processes command line args and puts a.\n"
  201.     "\t            PJL wrapper arouind the file and sends as is.  stiif is\n"
  202.     "\t            the default.\n"
  203.     "\n"
  204.     "\t[filename]  A file in appropriate image format.\n"
  205.     "\n");
  206.  
  207. }
  208.  
  209. /***********************************************************************/
  210.  
  211. void process_header_options(char* options)
  212.     /* ASSUMES: options is a null-terminated string containing
  213.      *          options in a command-line format
  214.      * EFFECTS: turns options into an argc and argv and calls
  215.      *          process_args on the resulting argument list.
  216.      *          Informs process_args that these are image header options,
  217.      *          so that process_args can apply proper precedence.
  218.      */
  219. {
  220.     int argc = 0;
  221.     char *argptr;
  222.     char *argv[MAXNUMARGS];
  223.  
  224.     argv[argc++] = ProgramName; /* set the initial argument */
  225.  
  226.     argptr = strtok(options, DELIMITER_STRING); /* get first argument */
  227.     while (argptr) {
  228.         argv[argc++] = argptr; /* save argument string ptr & incr count */
  229.         argptr = strtok(NULL, DELIMITER_STRING);  /* get next argument */
  230.     }
  231.  
  232.     optind = 1; /* Reset array pointer so that getopts starts over */
  233.  
  234.     process_args(argc, argv);
  235.  
  236. }
  237.  
  238. /***********************************************************************/
  239.  
  240. boolean process_args(int argc, char* argv[]) {
  241.  
  242.     /* ASSUMES:  argc, argv in valid format */
  243.     /* MODIFIES: has side effects on global state, like debug, files, */
  244.     /*           etc.. */
  245.     /* EFFECTS:  processes all the command line args using getopts. */
  246.     /*           Sets a bunch of state depending on which args are passed. */
  247.  
  248.     int c, ii, errflag = 0;
  249.  
  250.     opterr = 0; /* Turn off getopt debug messages, we'll print our own */
  251.  
  252.     while ((c = getopt(argc, argv, "P:F:JhstX:Y:S:n:d:j:T:R:DB:L:")) != -1) {
  253.         switch (c) {
  254.         case 'P':
  255.             PrinterName = strdup(optarg);
  256.             break;
  257.         case 'J':
  258.             SendPJL = FALSE;
  259.             pjlsetNoPJL(TRUE);
  260.             break;
  261.         case 'h':
  262.             print_usage_message();
  263.             exit(0);
  264.         case 's':
  265.             StatusOnly = TRUE;
  266.             break;
  267.         case 'X':
  268.             HorizRes = atoi(optarg);
  269.             break;
  270.         case 'Y':
  271.             VertRes = atoi(optarg);
  272.             break;
  273.         case 'S':
  274.             if (!strcasecmp(optarg,"BB+")) {  /* Full bleed B size for 4V */
  275.                 UserPageSize = PD_SIZE_B;
  276.                 FullBleed = TRUE;
  277.             } else if ((UserPageSize = PDGetSizeCodeByName(optarg)) < 0) {
  278.                 if (PDerrno != 0) {
  279.                     PDPerror(ProgramName);
  280.                 } else {
  281.                     fprintf(stderr, "%s: Invalid page size '%s' selected.\n",
  282.                                      ProgramName, optarg);
  283.                 }
  284.                 fprintf(stderr, "%s: Using default page size '%s'.\n",
  285.                        ProgramName, PDGetNameBySizeCode(LJ_DEFAULT_PAGE_SIZE));
  286.                 UserPageSize = LJ_DEFAULT_PAGE_SIZE;
  287.             }
  288.             break;
  289.         case 'n':
  290.             NumCopies = atoi(optarg);
  291.             break;
  292.         case 'd': /* Duplex option */
  293.             Duplex = LJ_DEFAULT_DUPLEX;
  294.             for (ii=0; ii<LJ_NUM_DUPLEX_NAMES; ii++) {
  295.                if (!strcasecmp(LJ_DUPLEX_NAMES[ii],optarg)) {
  296.                   Duplex = ii;
  297.                   break;
  298.                }
  299.             }
  300.             break;
  301.         case 'j': /* Darkness option */
  302.             Darkness = LJ_DEFAULT_DARKNESS;
  303.             for (ii=0; ii<LJ_NUM_DARKNESS_NAMES; ii++) {
  304.                if (!strcasecmp(LJ_DARKNESS_NAMES[ii],optarg)) {
  305.                    Darkness = ii;
  306.                    break;
  307.                }
  308.             }
  309.             break;
  310.         case 'T': /* Media Tray */
  311.             Tray = LJ_DEFAULT_TRAY;
  312.             for (ii=0; ii<LJ_NUM_TRAY_NAMES; ii++) {
  313.                if (!strcasecmp(LJ_TRAY_NAMES[ii],optarg)) {
  314.                   Tray = ii;
  315.                   break;
  316.                }
  317.             }
  318.             break;
  319.         case 'R': /* Output Tray */
  320.             OutBin = LJ_DEFAULT_OUTBIN;
  321.             for (ii=0; ii<LJ_NUM_OUTBIN_NAMES; ii++) {
  322.                if (!strcasecmp(LJ_OUTBIN_NAMES[ii],optarg)) {
  323.                   OutBin = ii;
  324.                   break;
  325.                }
  326.             }
  327.             break;
  328.         case 'F':  /* File format */
  329.             if (!strcasecmp("pcl",optarg)) pclFile = TRUE;
  330.             break;
  331.         case 'B':  /* How big a buffer to allocate for data */
  332.             if (atoi(optarg) <= 0) {
  333.                 fprintf(stderr, "%s: -%c (buffer size) option must have "
  334.                         "a positive argument.\n", ProgramName, (char)c);
  335.             } else {
  336.                 InBufSize = atoi(optarg);
  337.             }
  338.             break;
  339.         case 'D':
  340.             DEBUG++;  /* Multiple -D flags will increment this. */
  341.             /* Coarse, medium, and fine detail are invoked by */
  342.             /* one, two, and three or more -D flags respectively. */
  343.             switch (DEBUG) {
  344.             case 0:
  345.                 break;
  346.             case COARSEDETAIL:
  347.                 pjlSetDebug(TRUE);
  348.                 fprintf(stderr,"%s: Coarse detail debug messages enabled.\n",
  349.                         ProgramName);
  350.                 break;
  351.             case MEDIUMDETAIL:
  352.                 fprintf(stderr,"%s: Medium detail debug messages enabled.\n",
  353.                         ProgramName);
  354.                 break;
  355.             case FINEDETAIL:
  356.             default:
  357.                 fprintf(stderr, "%s: Fine detail debug messages enabled.\n",
  358.                         ProgramName);
  359.                 break;
  360.             }
  361.             break;
  362.         case '?':
  363.             /* Do NOT exit on bad options, just ignore them. */
  364.             fprintf(stderr, "%s: Ignoring an incomplete or "
  365.                     "unrecognized option '-%c'.\n", ProgramName, (char)optopt);
  366.             errflag++;
  367.             break;
  368.         }
  369.     }
  370.  
  371.     if (errflag) print_usage_message();
  372.  
  373.     /* Now check all the arguments. If wrong we sometimes will just fix
  374.      * them and continue
  375.      */
  376.  
  377.     /* Was a printer name specified?  If not, exit */
  378.  
  379.     if (PrinterName == NULL) {
  380.         fprintf(stderr, "%s: Printer name argument required:\n", ProgramName);
  381.         fprintf(stderr, "%s: \tYou must use the -P option to specify the "
  382.                 "printer name.\n", ProgramName);
  383.         print_usage_message();
  384.         return FALSE;
  385.     }
  386.  
  387.    /* Clamp reolution to 300 or 600 */
  388.  
  389.     if (HorizRes != VertRes) {
  390.         HorizRes = DEFAULT_RESOLUTION;
  391.         VertRes = DEFAULT_RESOLUTION;
  392.     } 
  393.     if ((HorizRes != 300) && (HorizRes != 600)) {
  394.         HorizRes = DEFAULT_RESOLUTION;
  395.     }
  396.     if ((VertRes != 300) && (VertRes != 600)) {
  397.         VertRes = DEFAULT_RESOLUTION;
  398.     }
  399.  
  400.     /* Keep within reason (PJL typically allows 999 as max) */
  401.  
  402.     if (NumCopies < 1) NumCopies = 1;
  403.     if (NumCopies > 999) NumCopies = 999;
  404.  
  405.     /* Was a filename specified after the last argument?
  406.      * If so, grab it and try to open it instead of stdin.
  407.      */
  408.  
  409.     if (optind < argc) {
  410.         if (freopen(argv[argc-1], "r", stdin) == NULL) {
  411.            fprintf(stderr,"%s:\tUnable to open input file %s for reading.\n",
  412.                    ProgramName, argv[argc-1]);
  413.            return FALSE;
  414.         } else {
  415.            if (DEBUG) {
  416.                fprintf(stderr, "%s: Reading from file '%s'.\n", 
  417.                        ProgramName, argv[argc-1]);
  418.            }
  419.         }
  420.     }
  421.  
  422.     return TRUE;
  423. }
  424.  
  425. /***********************************************************************/
  426.  
  427. int initialize_printer(FILE *out) {
  428.  
  429.     /* Send the PJL commands for options and then go into PCL mode */
  430.  
  431.  
  432.     if (pjlEnterPJLMode(out)              != LJ_EXIT_OK) return(FALSE);
  433.     if (pjlSetRes(out,HorizRes,VertRes)   != LJ_EXIT_OK) return(FALSE);
  434.     if (pjlSetPageProtect(out,FALSE)      != LJ_EXIT_OK) return(FALSE);
  435.     if (FullBleed) {
  436.         if (pjlSetPaperSize(out,-999) != LJ_EXIT_OK) return(FALSE);
  437.     } else {
  438.         if (pjlSetPaperSize(out,UserPageSize) != LJ_EXIT_OK) return(FALSE);
  439.     }
  440.     if (pjlSetCopies(out,NumCopies)       != LJ_EXIT_OK) return(FALSE);
  441.     if (pjlSetDuplex(out,Duplex)          != LJ_EXIT_OK) return(FALSE);
  442.     if (pjlSetTray(out,Tray)              != LJ_EXIT_OK) return(FALSE);
  443.     if (pjlSetOutBin(out,OutBin)          != LJ_EXIT_OK) return(FALSE);
  444.  
  445.     if (pjlSetDarkness(out,Darkness)      != LJ_EXIT_OK) return(FALSE);
  446.  
  447.     /* Now go into PCL mode */
  448.  
  449.     if (pjlEnterPCLMode(out)             != LJ_EXIT_OK)  return(FALSE);
  450.  
  451.     if (pclResetPrinter(out)             != LJ_EXIT_OK) return(FALSE);
  452.  
  453.     /* Set resolution here because need to do it in pcl for raster garphics */
  454.  
  455.     if (pclSetRes(out,HorizRes,VertRes)  != LJ_EXIT_OK) return(FALSE);
  456.  
  457.     /* Some models only allow tray via pcl */
  458.  
  459.     if (pclSetTray(out,Tray)             != LJ_EXIT_OK) return(FALSE);
  460.  
  461.     return(TRUE);
  462.  
  463. }
  464.  
  465. /***********************************************************************/
  466.  
  467. void cleanup_and_quit(const int exitcode) {
  468.  
  469.     /* ASSUMES:  Program should exit upon handling this error.
  470.      * MODIFIES: out
  471.      * EFFECTS:  Cleans up printer and exits.
  472.      *           Used to handle interrupts
  473.      */
  474.  
  475.     /* Called on normal, error and interrupt exit */
  476.  
  477.     if (DEBUG) {
  478.        fprintf(stderr,"%s: cleanup_and_quit called.\n",ProgramName);
  479.     }
  480.  
  481.     (void) pclCleanupPrinter(stdout);
  482.  
  483.     if (exitcode != NO_ERROR) {
  484.  
  485.        /* Drain any remaining input */
  486.  
  487.        if (InBuf) {
  488.             while (fgets(InBuf,InBufSize,infile))
  489.                     ;
  490.        }
  491.  
  492.     }
  493.  
  494.     if (ImageStream) (void) STClose(ImageStream);
  495.  
  496.     exit(exitcode);
  497.  
  498. }
  499.  
  500. /***********************************************************************/
  501.  
  502. void  setup_signal_handler(void) {
  503.  
  504.     /* REQUIRES: nothing
  505.      * MODIFIES: signal handling environment
  506.      * EFFECTS:
  507.      *
  508.      * Sets up signal handler to catch the killing signals
  509.      *  so that proper cleanup can be done
  510.      *
  511.      * This is important in the UNIX environment because many
  512.      * software interrupts are possible.  We catch the most common
  513.      * catchable interrupts.
  514.      *
  515.      * Cleanup_and_quit is a void(int ...) function which
  516.      * should clean up the printer and then exit the program.
  517.      */
  518.  
  519.     (void) signal(SIGINT, cleanup_and_quit);
  520.     (void) signal(SIGTERM, cleanup_and_quit);
  521.     (void) signal(SIGHUP, cleanup_and_quit);
  522.     (void) signal(SIGQUIT, cleanup_and_quit);
  523. }
  524.  
  525.  
  526. /***********************************************************************/
  527.  
  528. boolean read_image_header(STStream *datafile, PSTImageHeader *imghdr)
  529. {
  530.     /* ASSUMES:  datafile open for reading, current position is image header
  531.      * EFFECTS:  Reads and parses image file header,
  532.      *            leaving datafile pointer at begin of binary data.
  533.      *           Interprets any OPTIONS line, setting appropriate state.
  534.      *           Returns true if successful, else false.
  535.      * MODIFIES: values in imghdr
  536.      *           ALSO: Options line may alter any cmd-line settable variables.
  537.      * RETURNS:  TRUE if successful read and valid data format,
  538.      *           FALSE if failed read or invalid data format.
  539.      */
  540.  
  541.     if ( PSTReadImageHeader(datafile, imghdr) < 0 ) {
  542.         if (DEBUG > COARSEDETAIL) {
  543.             fprintf(stderr, "%s: Couldn't read image header:\n", ProgramName);
  544.             STPerror(ProgramName);
  545.         }
  546.         return FALSE;
  547.     }
  548.  
  549.     /* Read the image header for the print driver options (command
  550.      * line options can be passed as a TAG in a STIFF file).
  551.      */
  552.  
  553.     process_header_options(imghdr->driverOptions);
  554.  
  555.     return TRUE;
  556. }
  557.  
  558. /***********************************************************************/
  559.  
  560. void print_image_info(PSTImageHeader *imghdr) {
  561.  
  562.     /* REQUIRES: imghdr is valid
  563.      * MODIFIES: stderr
  564.      * EFFECTS:  writes image information to stderr
  565.      */
  566.  
  567.     /* Called if in debug mode */
  568.  
  569.     int img_type;
  570.  
  571.     switch (imghdr->type) {
  572.     case ST_TYPE_K:
  573.     case ST_TYPE_W:
  574.     case ST_TYPE_RGB:
  575.     case ST_TYPE_CMAP:
  576.     case ST_TYPE_MASK:
  577.     case ST_TYPE_SEP: /* synonym for TYPE_CMY and TYPE_CMYK */
  578.         img_type = imghdr->type;
  579.         break;
  580.     case ST_TYPE_YMC:
  581.         img_type = N_IMAGE_TYPES - 3;
  582.         break;
  583.     case ST_TYPE_YMCK:
  584.         img_type = N_IMAGE_TYPES - 2;
  585.         break;
  586.     default:
  587.         img_type = N_IMAGE_TYPES - 1;
  588.         break;
  589.     }
  590.  
  591.     fprintf(stderr, "%s: Input Image Header Information:\n", ProgramName);
  592.     fprintf(stderr, "\t xsize\t ysize\t zsize\t bpp\t type\t format\tbytes\n");
  593.     fprintf(stderr, "\t %lu\t %lu\t %d\t %d\t %s\t %s\t %lu\n",
  594.             imghdr->width, imghdr->height, (int)imghdr->samplesPerPixel,
  595.             (int)imghdr->bitsPerSample,
  596.             IMAGE_TYPES[ img_type ],
  597.             IMAGE_FORMATS[ imghdr->plane ],
  598.             imghdr->imgbytes
  599.             );
  600. }
  601.  
  602. /***********************************************************************/
  603. /* ARGSUSED */
  604.  
  605. boolean read_image_data(STStream* datafile, int imgbytes, unchar* pagebuf)
  606. {
  607.   /* ASSUMES:  image header has been successfully read,
  608.    *           datafile pointer is now pointing at first byte of data.
  609.    *           Pagebuf points to an area of memory of size pagebufsize.
  610.    * MODIFIES: datafile, possibly pagebuf and pagebufsize
  611.    * EFFECTS:  If pagebuf is NULL,
  612.    *            allocates page buffer of appropriate size,
  613.    *            sets pagebuf to point to it, and pagebufsize to indicate size.
  614.    *           If pagebufsize is too small, reallocates pagebuf to fit.
  615.    *           Finally, reads one image into memory starting at pagebuf.
  616.    * RETURNS:  TRUE if successful, FALSE if failed.
  617.    */
  618.  
  619.   long numbytesread = 0;
  620.  
  621.   /* Read the image data. */
  622.  
  623.   if ((numbytesread = STRead(datafile, (void*)pagebuf, imgbytes)) < 0) {
  624.       fprintf(stderr, "%s: ERROR: Failure while reading image data.\n",
  625.               ProgramName);
  626.       STPerror(ProgramName);
  627.       return FALSE;
  628.   }
  629.  
  630.   if (DEBUG) {
  631.       fprintf(stderr, "%s: Read  %8ld bytes from input file.\n",
  632.               ProgramName, numbytesread);
  633.   }
  634.  
  635.   /* Did we get what we expected? */
  636.  
  637.   if (numbytesread < imgbytes) {
  638.      fprintf(stderr, "%s: ERROR: Data file length (%ld) shorter than "
  639.              "header indicates (%lu).\n", ProgramName, numbytesread, imgbytes);
  640.       return FALSE;
  641.   }
  642.  
  643.   return TRUE;
  644. }
  645.  
  646. /***********************************************************************/
  647.  
  648. boolean is_valid_format(PSTImageHeader *imghdr ) {
  649.     /* ASSUMES:  image header has already been read
  650.      * MODIFIES: stderr (if debug mode set)
  651.      * EFFECTS:  returns TRUE if image is of proper format, else FALSE.
  652.      * NOTES:    Obviously, whether an image is valid is printer-specific.
  653.      */
  654.  
  655.     return (imghdr->plane == ST_PLANE_PACKED
  656.             && imghdr->samplesPerPixel == 1
  657.             && imghdr->type == ST_TYPE_K
  658.             && imghdr->bitsPerSample == 1
  659.             && imghdr->width > 0
  660.             && imghdr->height > 0
  661.             );
  662. }
  663.  
  664. /***********************************************************************/
  665.  
  666. boolean send_image(FILE *out, PSTImageHeader *imghdr, unchar *imagebuffer) 
  667. {
  668.     /* ASSUMES:  image header has been successfully read,
  669.      *           file pointer is now pointing at first byte of data
  670.      * MODIFIES: out
  671.      * RETURNS:  if successful, returns true, else false.
  672.      * EFFECTS:  Snags a row at a time out of the image,
  673.      */
  674.  
  675.     unchar* rowptr;               /* ptr to current location in page buffer */
  676.     unsigned long curline = 0;
  677.     unsigned long rowlength_bytes;
  678.     unsigned long maxrowsperread, nrowsread, rowstoread;
  679.  
  680.  
  681.     /* Initialize graphics for this page */
  682.  
  683.     if (pclPrepForGraphics(out) != LJ_EXIT_OK) {
  684.        fprintf(stderr,"%s: ERROR: Failed while initializing printer.\n",
  685.                ProgramName);
  686.        cleanup_and_quit(BAD_TRANSMISSION);
  687.     }
  688.  
  689.     /* Number of bytes in one row of data */
  690.  
  691.     rowlength_bytes = 1 + (imghdr->width - 1) / 8;
  692.  
  693.     /* Our buffer is probably not big enough to read whole image, calculate
  694.      * what we can read.
  695.      */
  696.     maxrowsperread = InBufSize / rowlength_bytes;
  697.     rowstoread = ImageHeader.height;
  698.     nrowsread = 0;
  699.  
  700.     /* Hold signals while we send one buffer full of data */
  701.  
  702.     hold_signals();
  703.  
  704.     /* Send the data */
  705.  
  706.     for (curline = 0; curline < ImageHeader.height; curline++) {
  707.  
  708.         if (nrowsread == 0) {
  709.  
  710.             nrowsread = rowstoread%maxrowsperread; /* do the odd piece first */
  711.  
  712.             /* nrowsread is always 0 after first time through (because of
  713.              * the MOD command done above).  Read MAX buffer then.
  714.              */
  715.             if (nrowsread == 0) nrowsread = maxrowsperread;
  716.  
  717.             if (DEBUG) fprintf(stderr, "%s: Reading image\n",ProgramName);
  718.  
  719.             if (!read_image_data(ImageStream, rowlength_bytes*nrowsread,
  720.                                  imagebuffer)) {
  721.                 cleanup_and_quit(BAD_DATA_FILE);
  722.             }
  723.  
  724.             /* Buffer holds more then one row so set up a ptr that can
  725.              * be incremented to point to start of each row.
  726.              */
  727.  
  728.             rowptr = imagebuffer;
  729.  
  730.             rowstoread -= nrowsread;
  731.         }
  732.  
  733.         if (pclSendRow(out, rowptr, rowlength_bytes)  != LJ_EXIT_OK) {
  734.                fprintf(stderr,"%s: ERROR while sending image data to "
  735.                        "printer.\n",ProgramName);
  736.                cleanup_and_quit(BAD_DATA_FORMAT);
  737.         }
  738.  
  739.         rowptr += rowlength_bytes;  /* Point to next row */
  740.         nrowsread--;                /* =0 when done with all rows in buffer */
  741.  
  742.     }
  743.  
  744.     (void) fflush(out);
  745.  
  746.     if (pclPrepForNextPage(out) != LJ_EXIT_OK) {
  747.        fprintf(stderr,"%s: ERROR: Failed while initializing printer.\n",
  748.                ProgramName);
  749.        cleanup_and_quit(BAD_TRANSMISSION);
  750.     }
  751.  
  752.     /* Release all the signals we were holding. */
  753.  
  754.     release_signals();
  755.  
  756.     return(TRUE);
  757. }
  758.  
  759. /***********************************************************************/
  760.  
  761. void add_error_message(const int error_code, const char* error_string) 
  762. {
  763.     /*
  764.      *  REQUIRES:
  765.      *          If error_string is DEFAULT_MSG,
  766.      *            error_code must be known to libpod.
  767.      *          If error_string is not DEFAULT_MSG,
  768.      *            error_string overrides default.
  769.      *
  770.      *  EFFECTS:
  771.      *      Appends one error to the global ErrMsgs array
  772.      *          and increments global variable pod->error_count by one.
  773.      *
  774.      *      This function adds an error code and corresponding message
  775.      *          to the active error status.  A message that matches the
  776.      *          error code is added to the structure along with the error
  777.      *          code.
  778.      *
  779.      *      If the number of messages would be increased over PD_MESSAGE_MAX,
  780.      *       then no message is added to the error_status struct.
  781.      *
  782.      *      If an error code is needed which does not appear in the database,
  783.      *       calling this function with non-null error_string allows the caller
  784.      *       complete control over the contents of the string area.
  785.      *      HOWEVER, this is NOT RECOMMENDED, as error codes and strings should
  786.      *       follow those in the libpod library for future compatibility.
  787.      *
  788.      *      DO NOT UNDER ANY CIRCUMSTANCES use codes which aren't POD-compliant
  789.      *       Make sure any codes chosen fit in the existing hierarchy of codes.
  790.      *
  791.      *  ASSUMPTIONS:
  792.      *          By convention, pod->active_status->error_count
  793.      *           is reset to 0 before each new set of error messages is added.
  794.      *
  795.      *          Assumes the following global variables:
  796.      *          static  PDInfoStruct  *pod;
  797.      *          static  PDMessageStruct ErrMsgs[PD_MESSAGE_MAX];
  798.      *
  799.      */
  800.  
  801.     if (Pod->active_status->error_count >= PD_MESSAGE_MAX) {
  802.         fprintf(stderr,
  803.            "%s: WARNING: POD message not added to database, has %d already.\n",
  804.             ProgramName, PD_MESSAGE_MAX);
  805.         return;
  806.     }
  807.  
  808.     if (error_string == DEFAULT_MSG) {
  809.  
  810.         /* Caller has chosen recommended path: Use default message. */
  811.  
  812.         PDMakeMessage(&ErrMsgs[Pod->active_status->error_count], error_code);
  813.  
  814.     } else {
  815.  
  816.         /* Caller has asked to use custom info.  Not recommended! */
  817.         /* First set the error code, then copy the string (carefully!) */
  818.  
  819.         ErrMsgs[Pod->active_status->error_count].message_code = error_code;
  820.  
  821.         (void) strncpy(ErrMsgs[Pod->active_status->error_count].message_text,
  822.                        error_string, PD_STR_MAX - 1 );
  823.  
  824.         ErrMsgs[Pod->active_status->error_count].message_text[PD_STR_MAX-1]
  825.             = '\0';
  826.  
  827.         ErrMsgs[Pod->active_status->error_count].message_code = error_code;
  828.     }
  829.  
  830.     /* NOTE: *DO NOT INCREMENT ERROR_COUNT IN THE CODE ABOVE*
  831.      * unless you absolutely MUST add multiple messages at once,
  832.      * because pod->error_count will be incremented by one on exit.
  833.      *
  834.      * The proper way to add multiple messages is to reset pod->error_count to
  835.      * zero, then call add_error_message multiple times, once with each message.     */
  836.  
  837.     ++Pod->active_status->error_count;
  838.  
  839.     return;
  840. }
  841.  
  842. /***********************************************************************/
  843.  
  844. int update_status(const int cur_status) 
  845. {
  846.     /* REQUIRES: parallel port open for reading
  847.      *           cur_status is what the printer status SHOULD be,
  848.      *           barring other errors....
  849.      * MODIFIES: active status file, stderr
  850.      * EFFECTS:  reads parallel port status and reports it into
  851.      *            printer active status file.
  852.      * ASSUMES:  We will not report more than PD_ERROR_MAX errors.
  853.      * RETURNS:  TRUE if reading status was successful, else FALSE.
  854.      */
  855.  
  856.     char    options[PD_STR_MAX];  /* What options we've detected */
  857.  
  858.     /* We hold signals here so we do not leave POD data base in a
  859.      * corrupted state in PDLocalWriteStatus
  860.      */
  861.  
  862.     hold_signals();
  863.  
  864.     Pod->active_status->operational_status = cur_status;
  865.     Pod->active_status->error_count = 0;
  866.  
  867.     /* If warnings info is allowed, add the optional driver version now.  */
  868.  
  869.     if (PrintWarnings) {
  870.         (void) add_error_message(PD_MSG_MASK_VERSION, LaserjetPJLDriverVersion);
  871.     }
  872.  
  873.     /* Set the page size to whatever the user has requested. */
  874.  
  875.     Pod->active_status->media_size = UserPageSize;
  876.  
  877.     /* Set the page size validation mask. We use the different
  878.      * masks to support different iresolutions
  879.      */
  880.  
  881.     switch(VertRes) {
  882.         case 600: 
  883.             if (FullBleed) {
  884.                Pod->active_status->validation_mask = PAGEMASK_BLEED_600;
  885.             } else {
  886.                Pod->active_status->validation_mask = PAGEMASK_600;
  887.             }
  888.             break;
  889.         default:
  890.         case 300:
  891.             if (FullBleed) {
  892.                Pod->active_status->validation_mask = PAGEMASK_BLEED_300;
  893.             } else {
  894.                Pod->active_status->validation_mask = PAGEMASK_300;
  895.             }
  896.             break;
  897.     }
  898.  
  899.     if (DEBUG >= MEDIUMDETAIL) {
  900.         fprintf(stderr, "%s: Setting validation mask to %d (%d dpi).\n",
  901.                 ProgramName, Pod->active_status->validation_mask,
  902.                 VertRes);
  903.     }
  904.  
  905.     /* Now handle reporting the options field: First null out the
  906.      * options field to allow filling it in using strncat 
  907.      */
  908.  
  909.     options[0] = '\0';
  910.  
  911.     /* Now report the resolution option selected: NOTE that the PS
  912.      * interpreter uses this information to override the POD .config
  913.      * file resolution, so that printers can switch resolution on the
  914.      * fly.  If the interpreter finds this option string in any POD
  915.      * .status file, it uses this resolution for its calculations and
  916.      * interpretation.
  917.      */
  918.  
  919.     (void) sprintf(options, "CurrentRes=%dx%d ", HorizRes, VertRes);
  920.  
  921.     /* NOTE TO DEVELOPER:
  922.      * Add your own options messages here, if necessary,
  923.      * by carefully strncat'ing additional strings onto options
  924.      * before we write them to the Pod->active_status structure, below
  925.      */
  926.  
  927.     /* Done setting our options string, copy it into Pod->active_status */
  928.  
  929.     (void) strncpy(Pod->active_status->printer_options, options, PD_STR_MAX-1);
  930.  
  931.     /* Now NULL terminate the string just in case */
  932.  
  933.     Pod->active_status->printer_options[PD_STR_MAX-1] = NULL;
  934.  
  935.     if (DEBUG) {
  936.         fprintf(stderr,"%s: Updating printer POD status file.\n", ProgramName);
  937.     }
  938.  
  939.     PDLocalWriteStatus(PrinterName, Pod->active_status, ErrMsgs);
  940.  
  941.     release_signals();
  942.  
  943.     return TRUE;
  944. }
  945.  
  946. /***********************************************************************/
  947.  
  948. void send_stiff_file(void)
  949.  
  950. {
  951.     unsigned int  imagenumber = 0;
  952.  
  953.     if (DEBUG) {
  954.        fprintf(stderr, "%s: Processing input STIFF file.\n", ProgramName);
  955.     }
  956.  
  957.     /* Open the image file for reading */
  958.  
  959.     ImageStream = STOpen(fileno(stdin), ST_READ);
  960.     if (!ImageStream) {
  961.         fprintf(stderr,"%s: ERROR: No data read from input stream!\n",
  962.                 ProgramName);
  963.         STPerror(ProgramName);
  964.         cleanup_and_quit(BAD_DATA_SHORT_FILE);
  965.     }
  966.  
  967.     /* Printer is successfully set up, let's start sending images to it.  */
  968.  
  969.     imagenumber = 0;
  970.     while (!feof(infile)) {
  971.  
  972.        imagenumber++;     /* keep track of which page we're sending */
  973.  
  974.         if (!read_image_header(ImageStream, &ImageHeader)) {
  975.            if (imagenumber == 1) {
  976.               fprintf(stderr,"%s: ERROR: Failure while reading image header.\n",                      ProgramName);
  977.               cleanup_and_quit(BAD_DATA_SHORT_FILE);
  978.            } else {
  979.               break; /* OK to run out of data after first page. */
  980.            }
  981.         }
  982.  
  983.         if (DEBUG) {
  984.            fprintf(stderr, "%s: Read header of image #%d.\n", ProgramName,
  985.                     imagenumber);
  986.            print_image_info(&ImageHeader);
  987.         }
  988.  
  989.         if (!is_valid_format(&ImageHeader)) {
  990.            fprintf(stderr, "%s: ERROR: Image not in STIFF packed 1-bit K "
  991.                    "format.\n", ProgramName);
  992.            fprintf(stderr, "\tOnly STIFF packed 1-bit K format is "
  993.                    " supported.\n\n");
  994.            cleanup_and_quit(BAD_DATA_FORMAT);
  995.         }
  996.  
  997.         if (imagenumber == 1) {
  998.  
  999.            /* initialize_printer is the first rotuine that sends output to
  1000.             * the printer.  We wait until we read the stiff file in case
  1001.             * it takes a long time for the file to be RIP'ed.  This is
  1002.             * important with network printers because we don't want to
  1003.             * make a network connection to a printer until we have image data
  1004.             * to send.  If we did, we would tie up the printer while we RIP
  1005.             * or the printer might time out if we do not send data to it
  1006.             * for some period of time (causing the connection to be broken
  1007.             * by the printer).
  1008.             */
  1009.  
  1010.             if (!initialize_printer(stdout)) {
  1011.                fprintf(stderr,"%s: ERROR: Failed to initialize printer.\n",
  1012.                        ProgramName);
  1013.                cleanup_and_quit(BAD_TRANSMISSION);
  1014.             }
  1015.         }
  1016.  
  1017.         if (!send_image(stdout, &ImageHeader, InBuf)) {
  1018.            fprintf(stderr, "%s: ERROR while sending page %d.\n",
  1019.                    ProgramName, imagenumber);
  1020.         }
  1021.  
  1022.         if (DEBUG) {
  1023.              fprintf(stderr, "%s: Finished sending image #%d.\n",
  1024.                      ProgramName, imagenumber);
  1025.         }
  1026.  
  1027.    } /* While */
  1028.  
  1029. }
  1030.  
  1031. /***********************************************************************/
  1032.  
  1033. void send_pcl_file(void)
  1034. {
  1035.    int        inbytesleft;             
  1036.    char*      ptr;
  1037.    boolean    empty_infile = FALSE;
  1038.    boolean    found_reset = FALSE;
  1039.  
  1040.    /* Send a file already in PCL mode  (used with fastpath by model file) */
  1041.  
  1042.    if (DEBUG) {
  1043.        fprintf(stderr, "%s: Processing input PCL file.\n", ProgramName);
  1044.    }
  1045.  
  1046.    if (!initialize_printer(stdout)) {
  1047.       fprintf(stderr,"%s: ERROR: Failed to initialize printer.\n",ProgramName);
  1048.       cleanup_and_quit(BAD_TRANSMISSION);
  1049.    }
  1050.  
  1051.    while (!feof(infile)) {
  1052.  
  1053.        inbytesleft = fread(InBuf,1,InBufSize,infile);
  1054.  
  1055.        if (inbytesleft > 0) empty_infile = FALSE;
  1056.     
  1057.        if (ferror(infile)) {
  1058.  
  1059.            fprintf(stderr, "%s: Error #%d reading input pipe, exiting.\n",
  1060.                    ProgramName, errno);
  1061.            cleanup_and_quit(BAD_DATA_SHORT_FILE);
  1062.        }
  1063.  
  1064.        if (!found_reset) {
  1065.  
  1066.           /* We have to strip the reset from the input path so it will
  1067.            * not reset the pcl commands we just prepended to the file 
  1068.            */
  1069.  
  1070.            if (ptr = strstr(InBuf,"\033E")) {
  1071.               *ptr=' ';
  1072.               *(ptr+1)=' ';
  1073.               found_reset=TRUE;
  1074.            } 
  1075.        }
  1076.  
  1077.        (void) fwrite(InBuf,1,inbytesleft,stdout);
  1078.  
  1079.        if (ferror(stdout)) {
  1080.           int exitcode = errno;
  1081.           fprintf(stderr, "%s: Error #%d writing output pipe.\n",
  1082.                   ProgramName, exitcode);
  1083.           cleanup_and_quit(BAD_TRANSMISSION);
  1084.        }
  1085.    }
  1086.  
  1087.    /* An empty input file is a downstream error, detect and exit with 
  1088.     * an error status.
  1089.     */
  1090.  
  1091.    if (empty_infile == TRUE) {
  1092.        fprintf(stderr, "%s Error: Input file empty\n",ProgramName);
  1093.        cleanup_and_quit(BAD_DATA_SHORT_FILE);
  1094.    }
  1095.  
  1096.    fflush(stdout);
  1097.  
  1098. }
  1099.  
  1100. /***********************************************************************/
  1101.  
  1102. int main(int argc, char* argv[]) 
  1103. {
  1104.  
  1105.     SLPrinterStruct *pinfo;      /* Printer info structure */
  1106.  
  1107.     /* First of all, set the global program name from argv[0]: */
  1108.  
  1109.     (void) set_program_name(argv[0]);
  1110.  
  1111.     /* Catch the killing signals so that proper cleanup can be done */
  1112.  
  1113.     (void) setup_signal_handler();
  1114.  
  1115.     /* Now handle all standard input flags according to SGI specifications */
  1116.  
  1117.     if (!process_args(argc, argv)) return(BAD_ARG);
  1118.  
  1119.     /* Set the model type based on the formal name of the printer.
  1120.      * Formal name comes from the model script file.  PJL and PCL
  1121.      * libraries need to know the model in use.
  1122.      */
  1123.  
  1124.     if ((SLGetPrinterInfo(PrinterName, &pinfo) == 0))  {
  1125.  
  1126.        if (DEBUG) {
  1127.           fprintf(stderr,
  1128.                   "%s: printer model=%s\n",ProgramName,pinfo->formal_name);
  1129.        }
  1130.  
  1131.        if (pjlSetModel(pinfo->formal_name) != LJ_EXIT_OK) {
  1132.           fprintf(stderr, "%s: Unknown printer model.  Assuming "
  1133.               "LaserJet 4Mv.\n",ProgramName);
  1134.        }
  1135.  
  1136.     }
  1137.  
  1138.     /* Get the model for our use.  If there was an error it will be
  1139.      * some default value.
  1140.      */
  1141.     PrinterModel = pjlGetModel();
  1142.  
  1143.     /* Open the POD database files. */
  1144.  
  1145.     if (PDLocalReadInfo(PrinterName, &Pod, &PodModTime) < 0)  {
  1146.         fprintf(stderr, "%s: Could not open required POD database "
  1147.                 " files for printer %s.\n", ProgramName, PrinterName);
  1148.         fprintf(stderr, "%s: Are you sure all required POD files are "
  1149.                 "properly installed?\n", ProgramName);
  1150.         PDPerror(ProgramName);
  1151.         cleanup_and_quit(BAD_POD_ACCESS);
  1152.     }
  1153.  
  1154.     if (DEBUG > MEDIUMDETAIL) {
  1155.         fprintf(stderr, "%s: Successfully read printer POD files.\n",
  1156.                 ProgramName);
  1157.     }
  1158.  
  1159.     /* If status-only is true, exit after updating status. */
  1160.  
  1161.     if (StatusOnly == TRUE)  exit(update_status(PD_STATUS_IDLE));
  1162.  
  1163.     /* Allocate a buffer to read input */
  1164.  
  1165.     if (InBufSize < 128) InBufSize = BUFDEFSIZE;
  1166.     InBuf = (unchar*) safeMalloc(InBufSize);
  1167.     if (!InBuf) {
  1168.         fprintf(stderr, "%s: Unable to allocate page buffer of %d bytes\n",
  1169.                 ProgramName, BUFDEFSIZE);
  1170.         cleanup_and_quit(BAD_MEMORY_ALLOC);
  1171.     }
  1172.  
  1173.     /* There is a command line option that indicates if the file is a 
  1174.      * PCL file.  If it is we tack on PJL and PCL commands to implement
  1175.      * command line options then we send the file.  Otherwise, the input
  1176.      * is a Stiff raster file and we convert it to PCL raster data and
  1177.      * send the PCL raster data to the printer.
  1178.      */
  1179.  
  1180.     if (pclFile) {
  1181.        send_pcl_file();
  1182.     } else {
  1183.        send_stiff_file();
  1184.     }
  1185.  
  1186.    cleanup_and_quit(NO_ERROR);
  1187.  
  1188.    /*NOTREACHED*/
  1189.  
  1190.    return 0;
  1191. }
  1192.